package com.bsj.power.pls.web;

import cn.dev33.satoken.annotation.SaIgnore;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.bsj.power.common.config.cached.LocationDeviceCached;
import com.bsj.power.common.config.global.JsonResult;
import com.bsj.power.common.config.global.JsonResultEnum;
import com.bsj.power.common.constant.NumericalConstants;
import com.bsj.power.common.constant.UWBPosConstant;
import com.bsj.power.common.def.dto.locationDevice.TagAlarmMsgDTO;
import com.bsj.power.common.def.dto.locationDevice.TagMsgDTO;
import com.bsj.power.common.def.entity.locationDevice.LocationDevice;
import com.bsj.power.common.def.entity.locationDevice.LocationDeviceLog;
import com.bsj.power.common.def.vo.pls.httpVo.HttpTagInfoVO;
import com.bsj.power.common.def.vo.pls.httpVo.HttpTagPosVO;
import com.bsj.power.common.def.vo.pls.httpVo.SimplifyPos;
import com.bsj.power.common.http.ExternalAccessHttp;
import com.bsj.power.common.mapper.LocationDeviceLogMapper;
import com.bsj.power.common.mapper.LocationDeviceMapper;
import com.bsj.power.common.util.DateUtils;
import com.bsj.power.common.util.ExceptionUtil;
import com.bsj.power.common.util.PosUtil;
import com.bsj.power.pls.config.cached.HighRiseCached;
import com.bsj.power.pls.file.uwbData.WRFileBaseUntil;
import com.bsj.power.pls.netty.CTInstructUtils;
import com.bsj.power.pls.netty.CachedChannelMap;
import lombok.extern.slf4j.Slf4j;
import net.dreamlu.iot.mqtt.core.server.MqttServer;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @Description ct柜机指令测试
 * @Author zhaoyang
 * @Date 2024/9/25 10:48
 */
@RequestMapping("/ctTest")
@RestController
@Slf4j
@SaIgnore
public class TestController {

    @Autowired
    private MqttServer mqttServer;

    @Value("${myUrl.ipHost}")
    private String ipHost;

    @Value("${ctUpgrade.filePath}")
    private String filePath;
    @Value("${ctUpgrade.MD5}")
    private String MD5;
    @Value("${ctUpgrade.fileLength}")
    private Integer fileLength;
    @Value("${ctUpgrade.httpHost}")
    private String httpHost;
    @Value("${ctUpgrade.httpPort}")
    private Integer httpPort;
    @Value("${ctUpgrade.fileName}")
    private String fileName;
    @Autowired
    private WRFileBaseUntil wrFileBaseUntil;

    @Autowired
    private LocationDeviceCached locationDeviceCached;

    @Resource
    private LocationDeviceLogMapper locationDeviceLogMapper;

    /**
     * 测试工牌弹出命令
     *
     * @param deviceId       设备id
     * @param stockSerialNum 仓位号
     * @return
     */
    @SaIgnore
    @RequestMapping("/test1")
    public JsonResult testCTInstructCmd(String deviceId, Integer stockSerialNum) {
        JsonResult jsonResult = new JsonResult();
        String clientId = CachedChannelMap.getClientId(deviceId);
        if (clientId == null) {
            jsonResult.setCode(-1);
            jsonResult.setMsg("设备不在线，设备id：" + deviceId);
            return jsonResult;
        }
        String cmd = CTInstructUtils.assembleUwbPopCmdStr(deviceId, stockSerialNum);
        mqttServer.publish(clientId, CTInstructUtils.getTopicSend(deviceId), cmd.getBytes());
        jsonResult.setCode(0);
        jsonResult.setMsg("已下发弹卡指令");
        jsonResult.setData(cmd);
        return jsonResult;
    }

    /**
     * 仓位子板信息查询
     *
     * @param deviceId 设备id
     * @return
     */
    @SaIgnore
    @RequestMapping("/stockInfoCmd")
    public JsonResult stockInfoCmd(String deviceId) {
        JsonResult jsonResult = new JsonResult();
        String clientId = CachedChannelMap.getClientId(deviceId);
        if (clientId == null) {
            jsonResult.setCode(-1);
            jsonResult.setMsg("设备不在线，设备id：" + deviceId);
            return jsonResult;
        }
        String cmd = CTInstructUtils.assembleStockInfoCmdStr();
        mqttServer.publish(clientId, CTInstructUtils.getTopicSend(deviceId), cmd.getBytes());
        jsonResult.setCode(0);
        jsonResult.setMsg("已下发仓位子板信息查询指令");
        jsonResult.setData(cmd);
        return jsonResult;
    }

    /**
     * 测试下发CQ设备状态查询指令
     *
     * @param deviceId       设备id
     * @param stockSerialNum 0 表示查询全部仓位信息；1~60 表示单独查询对应仓位的信息
     * @return
     */
    @SaIgnore
    @RequestMapping("/testCQ")
    public JsonResult testCTInstructCQCmd(String deviceId, Integer stockSerialNum) {
        String clientId = CachedChannelMap.getClientId(deviceId);
        if (clientId == null) {
            log.warn("设备离线：{}", deviceId);
            JsonResult jsonResult = new JsonResult();
            jsonResult.setCode(-1);
            jsonResult.setMsg("设备不在线，设备id：" + deviceId);
            return jsonResult;
        }
        mqttServer.publish(clientId, CTInstructUtils.getTopicSend(deviceId)
                , CTInstructUtils.assembleDevStateQueryCmd(deviceId, stockSerialNum));
        return JsonResult.success();
    }


    /**
     * 测试仓位禁用或启用控制命令
     *
     * @param deviceId       设备id
     * @param stockSerialNum 仓位号：范围 1~60
     * @param cmdParam       控制参数：1 表示禁用该仓位，0 表示启用该仓位
     * @return
     */
    @SaIgnore
    @RequestMapping("/testCL")
    public JsonResult testCTInstructCLCmd(String deviceId, Integer stockSerialNum, Integer cmdParam) {
        // 测试一个工牌弹出命令
        String clientId = CachedChannelMap.getClientId(deviceId);
        if (clientId == null) {
            log.warn("设备离线：{}", deviceId);
            JsonResult jsonResult = new JsonResult();
            jsonResult.setCode(-1);
            jsonResult.setMsg("设备不在线，设备id：" + deviceId);
            return jsonResult;
        }
        mqttServer.publish(clientId, CTInstructUtils.getTopicSend(deviceId)
                , CTInstructUtils.assembleStockDisableOrEnableCmd(deviceId, stockSerialNum, cmdParam));
        return JsonResult.success();
    }


    /**
     * 测试升级指令
     *
     * @param deviceId 设备id
     * @param opCode   操作码 1是升级升级主板MCU，10是升级叠加的所有子板MCU（升级好的子板，会自动跳过升级）
     * @return
     */
    @SaIgnore
    @RequestMapping("/ctUpgradeCmd")
    public JsonResult ctUpgradeCmd(String deviceId, Integer opCode) {
        // http://47.113.121.87:9992/pls/test/ctUpgradeCmd
        // http://192.168.130.11:8083/pls/test/ctUpgradeCmd 本地没有设备只能在测试环境测试
//        String cmdStr = "{OT,1,47.113.121.87,9992,TP_CT60_ETH_V1.240930.1,CB8210D319D39A1280010802CC6C7B3B,49700,1B36}";
        String clientId = CachedChannelMap.getClientId(deviceId);
        JsonResult jsonResult = new JsonResult();
        if (clientId == null) {
            log.warn("设备离线：{}", deviceId);
            jsonResult.setCode(-1);
            jsonResult.setMsg("设备不在线，设备id：" + deviceId);
            return jsonResult;
        }
        String cmd = CTInstructUtils.assembleFileUpgradeCmdStr(opCode, httpHost, httpPort, fileName, MD5, fileLength);
        mqttServer.publish(clientId, CTInstructUtils.getTopicSend(deviceId), cmd.getBytes());
        jsonResult.setCode(0);
        jsonResult.setMsg("已下发升级指令");
        jsonResult.setData(cmd);
        return jsonResult;
    }


    /**
     * 下载文件
     *
     * @param response
     * @param request
     * @return
     */
    @SaIgnore
    @RequestMapping(value = "/downloadFile")
    @CrossOrigin
    public void downloadFile(HttpServletResponse response, HttpServletRequest request) throws Exception {
        // 获取需要下载的文件的绝对路径和文件名
        // 服务器是 /mnt/data/ctupgradefile/TP_CT60_ETH_V1.240930.1-update.bin
        File file = new File(filePath);

        String filename = file.getName();
        log.info("CT下载文件名是：{}", filename);

        // 下载文件
        // 设置通用响应头
        response.reset();
        response.setHeader("content-type", "application/octet-stream");
        response.setContentType("application/octet-stream");
        response.setHeader("Content-Disposition", "attachment;fileName=" + filename);
        response.setStatus(HttpServletResponse.SC_OK);

        // 区分全量下载和断点续传
        // 1、断点续传
        String range = request.getHeader("Range");
        long position = 0L; // 起始位置
        long toLength = 0L; // 结束位置
        long contentLength = 0L; // 客户端请求内容长度
        int rangeSwitch = 0;
        long fileLength = file.length();

        if (range != null && range.trim().length() > 0 && !"null".equals(range)) {
            // 补充响应信息
            response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
            response.setHeader("Accept-Ranges", "bytes");

            // 0,从头开始的全文下载；1,从某字节开始的下载（bytes=27000-）；2,从某字节开始到某字节结束的下载（bytes=27000-39000）
            String rangBytes = range.replaceAll("bytes=", "");
            if (rangBytes.endsWith("-")) { // bytes=270000-
                rangeSwitch = 1;
                position = Long.parseLong(rangBytes.substring(0, rangBytes.indexOf("-"))); // substring():包头不包尾
                contentLength = fileLength - position; // 客户端请求的是270000之后的字节（包括bytes下标索引为270000的字节）
            } else { // bytes=270000-320000
                rangeSwitch = 2;
                String temp1 = rangBytes.substring(0, rangBytes.indexOf("-"));
                String temp2 = rangBytes.substring(rangBytes.indexOf("-") + 1, rangBytes.length());
                position = Long.parseLong(temp1);
                toLength = Long.parseLong(temp2);
                contentLength = toLength - position + 1; // 客户端请求的是 270000-320000 之间的字节
            }
        } else {
            contentLength = fileLength;
        }
        // 如果设设置了Content-Length，则客户端会自动进行多线程下载。如果不希望支持多线程，则不要设置这个参数。
        // Content-Length: [文件的总大小] - [客户端请求的下载的文件块的开始字节]
        response.setHeader("Content-Length", Long.toString(contentLength));

        // 准备流
        OutputStream outputStream = null;
        RandomAccessFile raf = null;

        // 断点开始
        // 响应的格式是:
        // Content-Range: bytes [文件块的开始字节]-[文件的总大小 - 1]/[文件的总大小]
        try {
            raf = new RandomAccessFile(file, "rw");
            outputStream = response.getOutputStream();
            // 补充响应信息
            if (rangeSwitch == 1) {
                String contentRange = "bytes " + position + "-" + (fileLength - 1) + "/" + fileLength;
                response.setHeader("Content-Range", contentRange);
                raf.skipBytes((int) position);
            } else if (rangeSwitch == 2) {
                String contentRange = range.replace("=", " ") + "/" + fileLength;
                response.setHeader("Content-Range", contentRange);
                raf.skipBytes((int) position);
            } else {
                String contentRange = "bytes " + "0-" + (fileLength - 1) + "/" + fileLength;
                response.setHeader("Content-Range", contentRange);
            }

            int n = 0;
            long readLength = 0;
            int size = 1024 * 1024 * 2;
            byte[] bytes = new byte[size];
            if (rangeSwitch == 2) {
                // 针对 bytes=27000-39000 的请求，从27000开始写数据
                while (readLength <= contentLength - size) {
                    n = raf.read(bytes);
                    readLength += n;
                    outputStream.write(bytes, 0, n);
                }
                if (readLength <= contentLength) {
                    n = raf.read(bytes, 0, (int) (contentLength - readLength));
                    outputStream.write(bytes, 0, n);
                }
            } else {
                while ((n = raf.read(bytes)) != -1) {
                    outputStream.write(bytes, 0, n);
                }
            }
            outputStream.flush();
        } catch (Exception e) {
            log.error("文件下载失败，文件名{}", filename);
        } finally {
            try {
                if (outputStream != null) {
                    outputStream.close();
                }
                if (raf != null) {
                    raf.close();
                }
            } catch (Exception e) {
                log.error("文件下载失败，文件名{}", filename);
            }
        }

    }


    /**
     * 测试设备数据透传
     *
     * @param id
     * @param hexStr
     * @return
     */
    @SaIgnore
    @RequestMapping("/testCmdPut")
    public JsonResult testCmdPut(String id, String hexStr) {
        boolean get = ExternalAccessHttp.transmitDataHttpPut("T", ipHost, id, hexStr);
        return JsonResult.success(get);
    }

    @SaIgnore
    @GetMapping("/testInRoad")
    public JsonResult testInRoad(String startTime, String endTime) {
        long sTime = DateUtils.strToDate(startTime).getTime();
        long eTime = DateUtils.strToDate(endTime).getTime();
        String deviceName = "14967288528204931179";
        String pathLineData = "[{\"x\":37.09895626145955,\"y\":16.5,\"z\":2.138536999772981},{\"x\":36.81296245744396,\"y\":16.5,\"z\":-36.87244512199825},{\"x\":27.955109754412888,\"y\":16.5,\"z\":-15.75550184777682},{\"x\":6.161964314444457,\"y\":16.5,\"z\":-15.546976196043758}]";
        Integer pathMapId = HighRiseCached.getFloorByMapId(2, 4);
        List<byte[]> listBytes = wrFileBaseUntil.readDataBytes(deviceName, sTime, eTime);
        int passIn = 0;
        List<SimplifyPos> simplifyPosList = new ArrayList<>();
        for (byte[] bytes : listBytes) {
            String data = new String(bytes);
            HttpTagPosVO posData = JSON.parseObject(data, HttpTagPosVO.class);
            //x,y,z
            double[] pos = posData.getPos();
            if (pos.length < 3) {
                continue;
            }
            SimplifyPos simplifyPos = SimplifyPos.builder().build();
            simplifyPos.sp(posData);
            simplifyPosList.add(simplifyPos);
        }
        ArrayList<SimplifyPos> simplifyPosArrayList = simplifyPosList.stream().collect(Collectors.collectingAndThen(
                Collectors.toMap(SimplifyPos::getPosStr, Function.identity(), (existing, replacement) -> existing),
                map -> new ArrayList<>(map.values())));
        for (SimplifyPos spPos : simplifyPosArrayList) {
            double[] reducePos = spPos.getReducePos();
            boolean inRoad = PosUtil.posInRoad(pathLineData, pathMapId, 3D, reducePos[0], -reducePos[1], spPos.getMapId());
            if (inRoad) {
                //达标
                passIn += 1;
            }
        }
        log.info("位置点: {}, 达标位置点: {}", simplifyPosArrayList.size(), passIn);
        return JsonResult.success(passIn);
    }

    @SaIgnore
    @PostMapping("/testDevice")
    public JsonResult testDevice(@RequestBody String str) {
        List<LocationDevice> locationDevices = new LinkedList<>();
        JSONObject jsonObject = JSONObject.parseObject(str);
        if (jsonObject.getInteger("code").equals(JsonResultEnum.SUCCESS.getCode())) {
            JSONArray jsonArray = jsonObject.getJSONArray("data");
            if (!CollectionUtils.isEmpty(jsonArray)) {
                List<HttpTagInfoVO> httpTagInfoVOS = jsonArray.toJavaList(HttpTagInfoVO.class);
                if (!CollectionUtils.isEmpty(httpTagInfoVOS)) {
                    log.info("标签总数量{}", httpTagInfoVOS.size());
                    locationDevices.addAll(httpTagInfoVOS.stream().map(httpTagInfoVO -> {
                        return new LocationDevice(httpTagInfoVO, NumericalConstants.TWO, NumericalConstants.THREE);
                    }).collect(Collectors.toMap(LocationDevice::getDeviceName, a -> a, (k1, k2) -> k1)).values().stream().collect(Collectors.toList()));
                }

            }
        }
        return JsonResult.success();
    }

//    @SaIgnore
//    @PostMapping("/testStatus")
//    public JsonResult testStatus(@RequestBody String str) {
//        log.info("WebSocketClientUntil接收消息内容: {}", str);
//        if (StringUtils.isNotEmpty(str)) {
//            JSONObject object = JSONObject.parseObject(str);
//            //14表示标签相关消息
//            Integer type = object.getInteger("type");
//            String data = object.getString("data");
//
//            //标签数据
//            if (type == 14) {
//                TagMsgDTO tagMsgDTO = JSONObject.parseObject(data, TagMsgDTO.class);
//                if (tagMsgDTO != null) {
//                    try {
//                        if (tagMsgDTO.getType() == UWBPosConstant.MSG_TYPE_STATUS) {
//                            log.info("标签状态数据打印：{}", tagMsgDTO);
//                        }
//                        //因为数据接收太快，只进行每秒一条数据存储
//                        tagMsgDTO.setNow(System.currentTimeMillis() / 1000);
//                        statusDataHandle(tagMsgDTO.getSn(), tagMsgDTO.getData());
//                    } catch (Exception e) {
//                        log.error("msgQueue写入数据失败:" + ExceptionUtil.getStackStr(e));
//                    }
//                }
//            } else if (type == 7) {
//                TagAlarmMsgDTO tagAlarmMsgDTO = com.alibaba.fastjson.JSONObject.parseObject(data, TagAlarmMsgDTO.class);
//                //电子围栏告警消息
//                if (tagAlarmMsgDTO != null && tagAlarmMsgDTO.getType() == 10) {
//                    try {
//                        log.info("接收到电子围栏报警信息：{}", tagAlarmMsgDTO);
////                        tagAlarmMsgQueue.push(tagAlarmMsgDTO);
//                    } catch (Exception e) {
//                        log.error("tagAlarmMsgQueue写入数据失败:" + ExceptionUtil.getStackStr(e));
//                    }
//                }
//            }
//
//        }
//        return JsonResult.success();
//    }

    /**
     * 标签状态数据处理
     *
     * @param sn   设备号
     * @param data 标签状态数据
     * @return void
     * @author ljx
     * @time 2024/12/5 19:42
     */
//    private void statusDataHandle(String sn, String data) {
//        JSONObject jsonObject = JSONObject.parseObject(data);
//        Boolean online = jsonObject.getBoolean("online");
//        if (online != null) {
//            LocationDevice locationDevice = locationDeviceCached.hgetLocationDeviceInfo(sn);
//            if (locationDevice == null) {
//                return;
//            }
//            //非基站设备
//            if (!locationDevice.getDeviceType().equals(NumericalConstants.ONE)) {
//                return;
//            }
//            LocationDeviceLog locationDeviceLog = new LocationDeviceLog(locationDevice);
//            if (online) {
//                locationDeviceLog.setType(NumericalConstants.TWO);
//            } else {
//                locationDeviceLog.setType(NumericalConstants.ONE);
//            }
//            locationDeviceLogMapper.insert(locationDeviceLog);
//        }
//    }

    public static void main(String[] args) throws Exception {
        HighRiseCached riseCached = new HighRiseCached();
        riseCached.start();
        Integer mapId = HighRiseCached.getFloorByMapId(2, 3);
        System.out.println(mapId);
    }


}
